package com.hclc.util.date;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import org.joda.time.DateTime;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 时间转换工具类
 *
 * @ClassName DateUtil
 * @Author: yurj
 * @Mail：1638234804@qq.com
 * @Date: Create in 8:51 2019-10-31
 * @version: 1.0
 */
public class DateUtil {
    /**
     * 结尾时间最小
     */
    private static final String MIN_ENDING_TIME_STR = " 00:00:00";

    /**
     * 结尾时间最大
     */
    private static final String MAX_ENDING_TIME_STR = " 23:59:59";

    /**
     * 年月日格式化
     */
    private static final String FORMAT_SPECIFIC_DATE = "yyyy/MM/dd";

    /**
     * 偏移量
     */
    private static final String OFFSET_ID = "+8";

    /**
     * 时间戳长度
     */
    private static final int TIMESTAMP_LENGTH = 10;

    /**
     * 时间字符串长度
     */
    private static final int TIME_STR_LENGTH = 10;

    /**
     * 将 LocalDateTime转换为时间戳
     *
     * @param time
     * @return java.lang.Long
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2019-10-31 8:57
     */

    public static long getTimestampByLocalDateTime(LocalDateTime time) {
        return time.toInstant(ZoneOffset.of(OFFSET_ID)).toEpochMilli();
    }

    /**
     * 将 LocalDate转换为时间戳
     *
     * @param time
     * @return long
     * @author ljj
     * @version 1.0
     * @date 2020/10/19 14:14
     */
    public static Long getTimestampByLocalDate(LocalDate time) {
        if (Objects.isNull(time)) {
            return null;
        }
        return getTimestampByLocalDateTime(time.atStartOfDay());
    }

    /**
     * 将时间戳转换为LocalDateTime
     *
     * @param timestamp
     * @return java.time.LocalDateTime
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2019-10-31 8:54
     */

    public static LocalDateTime getLocalDateTimeByLong(long timestamp) {
        if (String.valueOf(timestamp).length() != TIMESTAMP_LENGTH) {
            timestamp = timestamp / 1000;
        }
        return LocalDateTime.ofEpochSecond(timestamp, 0, ZoneOffset.ofHours(8));
    }

    /**
     * 分钟为一位数的时候十位数补0
     *
     * @param str
     * @return java.lang.String
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2019-11-16 20:27
     */

    public static String patchPosition(String str) {
        if (str.length() == 1) {
            return "0" + str;
        }
        return str;
    }

    /**
     * 格式化为 2020-3-15 00:00:00
     *
     * @param
     * @return java.lang.String
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020-3-15 9:56
     */
    public static String formatDateByLocalDate(LocalDate time, TimeTypeEnum timeTypeEnum) {
        //格式化es所需格式
        DateTimeFormatter df = DateTimeFormatter.ofPattern(FORMAT_SPECIFIC_DATE);
        if (TimeTypeEnum.START_TIME.equals(timeTypeEnum)) {
            // LocalTime.MIN和LocalTime.MAX 格式有问题，会查询不出来数据
            return df.format(time) + MIN_ENDING_TIME_STR;
        }
        return df.format(time) + MAX_ENDING_TIME_STR;
    }

    /**
     * 时间类型枚举
     */
    public enum TimeTypeEnum {


        START_TIME(0, "开始时间"),


        END_TIME(1, "结束时间");


        @JsonValue
        private final int code;


        private final String desc;

        TimeTypeEnum(int code, String desc) {
            this.code = code;
            this.desc = desc;
        }

        public String getDesc() {
            return desc;
        }

        public static TimeTypeEnum find(String desc) throws IllegalArgumentException {
            for (TimeTypeEnum model : TimeTypeEnum.values()) {
                if (model.getDesc().equals(desc)) {
                    return model;
                }
            }
            throw new IllegalArgumentException();
        }

        @JsonCreator
        public static TimeTypeEnum find(int code) throws IllegalArgumentException {
            for (TimeTypeEnum model : TimeTypeEnum.values()) {
                if (model.code == code) {
                    return model;
                }
            }
            throw new IllegalArgumentException();
        }
    }

    /**
     * 开始时间和结束时间解析类
     */
    public static class WeekData {
        // 一周的开始时间
        public LocalDate start;
        // 一周的结束时间
        public LocalDate end;

        public WeekData(List<LocalDate> localDates) {
            this.start = localDates.get(0);
            this.end = localDates.get(localDates.size() - 1);
        }

    }

    /**
     * 获取某个月的每周的开始日期和结束日期
     *
     * @param
     * @return java.util.Map<java.lang.Integer, com.hclc.util.DateUtil.WeekData>
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020-3-15 21:44
     */

    public static Map<Integer, WeekData> getStartAndEndDatesOfTheWeekByYearMonth(LocalDate yearMoth) {
        LocalDate start = LocalDate.now().with(yearMoth).with(TemporalAdjusters.firstDayOfMonth());
        LocalDate end = LocalDate.now().with(yearMoth).with(TemporalAdjusters.lastDayOfMonth());

        Map<Integer, WeekData> map = Stream.iterate(start, localDate -> localDate.plusDays(1L))
                .limit(ChronoUnit.DAYS.between(start, end) + 1)
                .collect(Collectors.groupingBy(localDate -> localDate.get(WeekFields.of(DayOfWeek.MONDAY, 1).weekOfMonth()),
                        Collectors.collectingAndThen(Collectors.toList(), WeekData::new)));
        return map;
    }

    /**
     * 根据指定DateTime获取当前是第几周
     *
     * @param time
     * @return java.time.LocalDate
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020-3-15 22:01
     */

    public static int getWeekNumByDateTime(DateTime time) {
        return LocalDate.of(time.getYear(), time.getMonthOfYear(), time.getDayOfMonth())
                .get(WeekFields.of(DayOfWeek.MONDAY, 1).weekOfMonth());
    }


    /**
     * 字符串格式化为LocalDate
     *
     * @param localDateStr
     * @return java.time.LocalDate
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020-4-10 17:07
     */

    public static LocalDate parseLocalDateByStr(String localDateStr) {
        String dateFormat;
        if (localDateStr.length() == TIME_STR_LENGTH) {
            dateFormat = "yyyy-MM-dd";
        } else {
            dateFormat = "yyyy-M-d";
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
        return LocalDate.parse(localDateStr, formatter);
    }


    /**
     * 周期类型
     */
    public enum CycleEnum {
        YEAR(1, "年"),

        MONTH(2, "月"),

        WEEK(3, "周"),

        DAY(4, "日");

        @JsonValue
        private int code;

        private String desc;

        CycleEnum(int code, String desc) {
            this.code = code;
            this.desc = desc;
        }


        @JsonCreator
        public static StatisCycleEnum find(int code) throws IllegalArgumentException {
            for (StatisCycleEnum model : StatisCycleEnum.values()) {
                if (model.code == code) {
                    return model;
                }
            }
            throw new IllegalArgumentException();
        }
    }

    public enum StatisCycleEnum {
        MONTH(1, "月"),

        WEEK(2, "周"),

        DAY(3, "日");

        @JsonValue
        private int code;

        private String desc;

        StatisCycleEnum(int code, String desc) {
            this.code = code;
            this.desc = desc;
        }


        @JsonCreator
        public static StatisCycleEnum find(int code) throws IllegalArgumentException {
            for (StatisCycleEnum model : StatisCycleEnum.values()) {
                if (model.code == code) {
                    return model;
                }
            }
            throw new IllegalArgumentException();
        }
    }

    /**
     * @param cycleEnum
     * @return java.util.List<java.time.LocalDate>
     * @Description: TODO(根据周期获取时间范围)
     * @author hef
     * @version 1.0
     * @date 2020-05-18 09:50
     */
    public static List<LocalDate> createTimeRangeForCycle(CycleEnum cycleEnum) {
        List<LocalDate> dates = new ArrayList<>(2);
        LocalDate startDate = null;
        LocalDate endDate = null;
        switch (cycleEnum) {
            //年
            case YEAR: {
                LocalDate now = LocalDate.now();
                int year = now.getYear();
                endDate = LocalDate.of(year, 12, 1);
                int maxDay = endDate.getMonth().maxLength();

                startDate = LocalDate.of(year, 1, 1);
                endDate = LocalDate.of(year, 12, maxDay);
                break;
            }
            //月
            case MONTH: {
                LocalDate now = LocalDate.now();
                int monthValue = now.getMonthValue();
                int maxDay = now.getMonth().maxLength();
                startDate = LocalDate.of(now.getYear(), monthValue, 1);
                endDate = LocalDate.of(now.getYear(), monthValue, maxDay);
                break;
            }
            //周
            case WEEK: {
                LocalDate now = LocalDate.now();
                int today = LocalDate.now().getDayOfWeek().getValue();
                //这周的第一天(星期一)
                startDate = now.minusDays(today - 1);
                endDate = startDate.plusDays(6);
                break;
            }
            //天
            case DAY: {
                startDate = LocalDate.now();
                endDate = LocalDate.now();
                break;
            }
            default: {
                break;
            }
        }
        dates.add(startDate);
        dates.add(endDate);
        return dates;
    }

    /**
     * 判断当前是否是闰年
     *
     * @param prolepticYear
     * @return boolean
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2019-11-29 16:38
     */

    public static boolean isLeapYear(long prolepticYear) {
        return ((prolepticYear & 3) == 0) && ((prolepticYear % 100) != 0 || (prolepticYear % 400) == 0);
    }


    /**
     * 计算两个日期之间天数差
     *
     * @param startTime
     * @param endTime
     * @return long
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/7/22 19:26
     */

    public static long getDateDifference(LocalDate startTime, LocalDate endTime) {
        return endTime.toEpochDay() - startTime.toEpochDay();
    }

    /**
     * 根据指定单位计算两个localTime时间差
     *
     * @param interval
     * @param unit
     * @param startTime
     * @param endTime
     * @return long
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/8/9 16:32
     */
    public static long getDateDifferenceByUnit(int interval, TimeUnit unit, LocalTime startTime, LocalTime endTime) {
        Duration duration = Duration.between(startTime, endTime);
        long unitVal = 0L;
        switch (unit) {
            case DAYS:
                unitVal = duration.toDays();
                break;
            case HOURS:
                unitVal = duration.toHours();
                break;
            case MINUTES:
                unitVal = duration.toMinutes();
                break;
            case SECONDS:
                unitVal = duration.toMinutes() * 60;
                break;
            case MILLISECONDS:
                unitVal = duration.toMillis();
                break;
            default:
                break;
        }
        long unitsCount = unitVal / interval;
        return unitsCount;
    }

    /**
     * 根据类型获取指定日期起始时间戳
     *
     * @param timeTypeEnum
     * @return long
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020/8/10 10:58
     */
    public static long getCurrentDayTimestampByTimeType(LocalDate localDate, TimeTypeEnum timeTypeEnum) {
        if (TimeTypeEnum.START_TIME.equals(timeTypeEnum)) {
            return getTimestampByLocalDateTime(LocalDateTime.of(localDate, LocalTime.MIN));
        }
        return getTimestampByLocalDateTime(LocalDateTime.of(localDate, LocalTime.MAX));
    }

    public static void main(String[] args) {
        System.out.println(getDateDifference(LocalDate.of(2020, 2, 29), LocalDate.of(2020, 4, 29)));
        System.out.println(getLocalDateTimeByLong(1596440890772L));
        System.out.println(getDateDifferenceByUnit(1, TimeUnit.MILLISECONDS, LocalTime.of(16, 40), LocalTime.of(16, 48)));

        System.out.println(getCurrentDayTimestampByTimeType(LocalDate.now(), TimeTypeEnum.START_TIME));
        System.out.println(getCurrentDayTimestampByTimeType(LocalDate.now(), TimeTypeEnum.END_TIME));
//        Map<Integer, DateUtil.WeekData> weeks = DateUtil.getStartAndEndDatesOfTheWeekByYearMonth(LocalDate.now().minusMonths(1));
//        System.out.println(weeks.get(1).end);
    }
//    public static void main(String[] args) {
//       /* LocalDateTime endTime =  LocalDateTime.of(LocalDate.now().minusMonths(1).with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
//        LocalDateTime  startTime = LocalDateTime.of(LocalDate.now().minusMonths(12).with(TemporalAdjusters.firstDayOfMonth()),LocalTime.MIN);
//        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy/MM/dd");
//        System.out.println(df.format(startTime)+" "+LocalTime.MIN);
//        System.out.println(df.format(endTime)+" "+LocalTime.MAX);
//        System.out.println(time.getDayOfMonth());*/
//        ;
//        System.out.println(LocalDate.of(2020, 3, 22).with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth());
//
//
//        //开始时间：一年前
//        //LocalDate startTime = LocalDate.now().minusMonths(YEAR).with(TemporalAdjusters.firstDayOfMonth());
//        //结束时间：上个月
//        //LocalDate endTime = LocalDate.now().minusMonths(ONE).with(TemporalAdjusters.lastDayOfMonth());
//
//        //System.out.println(formatDateByLocalDate(LocalDate.now().minusMonths(1).with(TemporalAdjusters.firstDayOfMonth()),TimeTypeEnum.START_TIME) );
//        //System.out.println(formatDateByLocalDate(LocalDate.now().minusMonths(1).with(TemporalAdjusters.lastDayOfMonth()),TimeTypeEnum.END_TIME) );
//        System.out.println(LocalDate.now().with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.MONDAY)));
//        System.out.println(LocalDate.now().with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.SUNDAY)));
//
//
//        System.out.println(LocalDate.now().plusMonths(1).with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.MONDAY)));
//        System.out.println(LocalDate.now().plusMonths(1).with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.SUNDAY)));
//        // 随意取一个时间，取了当前时间
//        LocalDate localDate = LocalDate.now();
//        System.out.println("当前时间为：" + localDate);
//
//// 根据封装好的月份获取一个一月到十二月的Month流，此时流里的对象为Month
//        Arrays.stream(Month.values())
//                // 把每个month都调整到当前这个时间里，此时流的对象为LocalDate
//                .map(month -> month.adjustInto(localDate))
//                // 这里方便里观察此时流的数据，把转换后的时间打印了出来
//                .peek(System.out::println)
//                // 根据时间的所在月的第一天和最后一天作为间隔计算周数
//                .map(temporal -> ChronoUnit.WEEKS.between(temporal.with(TemporalAdjusters.firstDayOfMonth()), temporal.with(TemporalAdjusters.lastDayOfMonth())))
//                // 打印最后的周数
//                .forEach(System.out::println);
//
//    }
}
